package docker

import (
	"fmt"
	. "pdocker/tool/docker"
	"reflect"
	"strings"

	dockertype "github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/go-connections/nat"
	log "github.com/sirupsen/logrus"
)

func GetContainer(filter string) (*dockertype.Container, error) {
	c1, e1 := GetContainerById(filter)
	c2, e2 := GetContainerByName(filter)
	if !reflect.DeepEqual(c1, &dockertype.Container{}) && e1 == nil {

		log.Printf("Image: %s Container ID %s \n", c1.Image, filter)
		return c1, e1
	} else if !reflect.DeepEqual(c2, &dockertype.Container{}) && e2 == nil {
		log.Printf("Image: %s Container Name %s \n", c2.Image, filter)
		return c2, e2
	} else if e1 != nil && e2 != nil {
		return &dockertype.Container{}, fmt.Errorf("%s %s", e1, e2)
	}
	return &dockertype.Container{}, nil

}

func GetContainerByName(name string) (*dockertype.Container, error) {
	containers, err := Cli.ContainerList(Ctx, dockertype.ContainerListOptions{
		Size:    true,
		All:     true,
		Filters: filters.NewArgs(filters.Arg("name", name)),
	})
	if err != nil {
		return &dockertype.Container{}, err
	} else if len(containers) == 0 {
		return &dockertype.Container{}, fmt.Errorf("Container Name: %s not found \t", name)
	} else {
		return &containers[0], err
	}
}

func GetContainerById(id string) (*dockertype.Container, error) {
	containers, err := Cli.ContainerList(Ctx, dockertype.ContainerListOptions{
		Size:    true,
		All:     true,
		Filters: filters.NewArgs(filters.Arg("id", id)),
	})
	if err != nil {
		return &dockertype.Container{}, err
	} else if len(containers) == 0 {
		return &dockertype.Container{}, fmt.Errorf("Container Id: %s not found ", id)
	} else {
		return &containers[0], err
	}
}

func ShowContainers(all bool) {
	containers, err := Cli.ContainerList(Ctx, dockertype.ContainerListOptions{
		All: all,
	})
	if err != nil {
		panic(err)
	}

	for _, c := range containers {
		_, ok := c.NetworkSettings.Networks["sebafi-pro_sebafi"]
		if !strings.Contains(c.Image, "harbor") && !ok {
			ShowContainer(&c)
		}
	}
}

func ShowContainer(container *dockertype.Container) {
	fmt.Printf("%s %s %s \n", container.ID, container.Names[0][1:], container.Image)
	for key, ns := range container.NetworkSettings.Networks {
		fmt.Printf("%s -> %s %s %s ", key, ns.IPAddress, ns.Gateway, ns.NetworkID)
	}

	for _, port := range container.Ports {
		fmt.Printf("%d \n", port.PublicPort)
	}
	fmt.Println()
}

func GetContainerIP(container *dockertype.Container) string {
	for _, ns := range container.NetworkSettings.Networks {
		return ns.IPAddress
	}
	return ""
}

func CreateContainer(image, name string, flags, ports []string) (string, error) {
	r, e := GetImage(image)
	if !r {
		//err := pullImage(image)
		//if err != nil {
		//	return "", err
		//}
		return "", e

	}
	log.Printf("%s Start", image)
	resp, err := Cli.ContainerCreate(Ctx, &container.Config{
		Image: image,
		Env: func() []string {
			if len(flags) != 0 {
				return []string{fmt.Sprintf("flag=%s", flags[0])}
			} else {
				return []string{}
			}
		}(),
		ExposedPorts: func() nat.PortSet {
			portSet := nat.PortSet{}
			for _, port := range ports {
				portSet[nat.Port(port)] = struct{}{}
			}
			return portSet
		}(),
	}, &container.HostConfig{PortBindings: func() nat.PortMap {
		portMap := nat.PortMap{}
		for _, port := range ports {
			portMap[nat.Port(port)] = []nat.PortBinding{{}}
		}
		return portMap
	}()}, &network.NetworkingConfig{}, name)

	return resp.ID, err
}

func StartContainer(ID string) error {
	err := Cli.ContainerStart(Ctx, ID, dockertype.ContainerStartOptions{})
	if err != nil {
	} else {
		log.Printf("%8s is Running", ID)
	}
	//n, err := Cli.NetworkList(
	//	Ctx, dockertype.NetworkListOptions{
	//		Filters: filters.NewArgs(filters.Arg("name", "test1"))})
	//fmt.Printf("test1: %s \n", n[0].ID)
	//err = Cli.NetworkConnect(Ctx, n[0].ID, ID, nil)
	return err
}

func DeleteDocker(id string) error {
	c, err := GetContainerById(id)
	if err != nil {
		log.Errorln("Delete Error: ", err.Error())
		return err
	} else {
		err = Cli.ContainerStop(Ctx, id, nil)
		if err != nil {
			log.Errorln("Stop Error: ", err.Error())
			return err
		} else {
			err := Cli.ContainerRemove(Ctx, c.ID, dockertype.ContainerRemoveOptions{})
			if err != nil {
				log.Errorln("Delete Error: ", err.Error())
				return err
			}
		}
	}
	return nil
}

func DeleteContainer(id string, callback bool) error {
	log.Printf("Delete %s ", id)
	c, err := GetContainerById(id)
	if err != nil {
		log.Warningf("%s", err)

	} else {
		err := Cli.ContainerStop(Ctx, c.ID, nil)
		if err != nil {
			log.Errorf("%s", err)
		} else {
			log.Debugf("%10s | %s", "Container Stop", id)
			err := Cli.ContainerRemove(Ctx, c.ID, dockertype.ContainerRemoveOptions{})
			if err != nil {
				log.Errorf("Error: %s", err)
			} else {
				log.Debugf("%10s | %s", "Container Remove", id)
			}
		}
	}

	//if callback {
	//	resp, _ := http.Get(
	//		//fmt.Sprintf("%s?name=%s", config.Config.Docker.TimeoutCallBack, id))
	//	defer resp.Body.Close()
	//	body, err := ioutil.ReadAll(resp.Body)
	//	if err != nil {
	//	}
	//	if len(body) < 1000 {
	//		//log.Printf("Get: %s Return: %s \n", config.Config.Docker.TimeoutCallBack, string(body))
	//	} else {
	//		match, _ := xmlpath.Compile("//h1")
	//		errorHTML, _ := xmlpath.Parse(resp.Body)
	//		if value, ok := match.String(errorHTML); ok {
	//			log.Println(value)
	//		} else {
	//			//log.Println(string(body))
	//		}
	//
	//	}
	//}
	return err

}

func CreateLabContainer(labConfig LabDocker) (string, error) {
	exist, err := GetImage(labConfig.Image)
	if !exist {
		return "", err
	}
	log.Infof("%s start", labConfig.Image)
	var c = &container.Config{
		Image:           labConfig.Image,
		AttachStderr:    labConfig.AttachStderr,
		AttachStdin:     labConfig.AttachStdin,
		AttachStdout:    labConfig.AttachStdout,
		Tty:             labConfig.Tty,
		OpenStdin:       labConfig.OpenStdin,
		StdinOnce:       labConfig.StdinOnce,
		Env:             labConfig.Env,
		ArgsEscaped:     labConfig.ArgsEscaped,
		WorkingDir:      labConfig.WorkingDir,
		Entrypoint:      labConfig.Entrypoint,
		NetworkDisabled: labConfig.NetworkDisabled,
		Shell:           labConfig.Shell,
		Cmd:             labConfig.Cmd,
		ExposedPorts: func() nat.PortSet {
			portSet := nat.PortSet{}
			for _, port := range labConfig.Ports {
				portSet[nat.Port(port)] = struct{}{}
			}
			return portSet
		}(),
	}
	resp, err := Cli.ContainerCreate(Ctx, c, &container.HostConfig{
		Privileged: labConfig.Privileged,
		PortBindings: func() nat.PortMap {
			portMap := nat.PortMap{}
			for _, port := range labConfig.Ports {
				portMap[nat.Port(port)] = []nat.PortBinding{{}}
			}
			return portMap
		}(),
	}, &network.NetworkingConfig{}, labConfig.Name)
	return resp.ID, err
}
